﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using ABCat.Core;
using ABCat.Core.Plugins;
using ABCat.Core.Plugins.Catalog;
using ABCat.Core.Plugins.DataProviders;
using ABCat.Core.Plugins.DataSets;

namespace ABCat.Plugins.NormalizationLogic.Standard
{
	[PluginCreator("Standard normalization logic", WorkModeEnum.PerContext)]
	public class NormalizationLogicStandardPlugin : INormalizationLogicPlugin
	{
		#region + Events +

		public event EventHandler Disposed;

		#endregion
		#region + Fields +

		private readonly ConcurrentDictionary<string, string> _authors = new ConcurrentDictionary<string, string>();

		private Context _context;

		#endregion
		#region + Properties +

		public Config Config		{get; set;}
		public Context Context
		{
			get { return _context; }
			protected set
			{
				if (_context == value) return;
				_context = value;
				_authors.Clear();
				using (var dbContainer = _context.CreateDbContainer(false))
				{
					var allBooks = dbContainer.AudioBookSet.GetRecordsAll();
					foreach (var audioBook in allBooks)
					{
						_authors.TryAdd(audioBook.Author.ToLower(), audioBook.Author);
					}
				}
			}
		}

		#endregion
		#region + Ctor +

		public NormalizationLogicStandardPlugin(Context context)
		{
			Context = context;
		}

		#endregion
		#region + Logic +

		public virtual string CalcAuthorName(IAudioBook record)
		{
			string result = record.Author;

			if (string.IsNullOrEmpty(result))
			{
				if (!string.IsNullOrEmpty(record.AuthorNameForParse) && !string.IsNullOrEmpty(record.AuthorSurnameForParse))
				{
					result = "{0}, {1}".F(record.AuthorSurnameForParse, record.AuthorNameForParse);
				}
				else if (string.IsNullOrEmpty(record.AuthorNameForParse) && string.IsNullOrEmpty(record.AuthorSurnameForParse))
				{
					result = Context.AuthorDefaultName;
				}
				else
				{
					result = record.AuthorSurnameForParse + record.AuthorNameForParse;
				}
			}

			return result;
		}

		public bool CheckForConfig(bool correct, out Config incorrectConfig)
		{
			incorrectConfig = null;
			return true;
		}

		public void Dispose()
		{
			Disposed.Fire(this);
		}

		public void Normalize(IEnumerable<IAudioBook> records, IDbContainer dbContainer)
		{
			var recordArray = records as IAudioBook[] ?? records.ToArray();
			var cache = dbContainer.ReplacementStringSet.GetReplacementStringsAll().ToArray();

			foreach (var record in recordArray)
			{
				var bitrateReplacement = cache.FirstOrDefault(str => str.RecordPropertyName == "bitrate" && String.Compare(str.PossibleValue, record.Bitrate, StringComparison.OrdinalIgnoreCase) == 0);
				if (bitrateReplacement != null) record.Bitrate = bitrateReplacement.ReplaceValue;

				var publisherReplacement = cache.FirstOrDefault(str => str.RecordPropertyName == "publisher" && String.Compare(str.PossibleValue, record.Publisher, StringComparison.OrdinalIgnoreCase) == 0);
				if (publisherReplacement != null) record.Publisher = publisherReplacement.ReplaceValue;

				var readerReplacement = cache.FirstOrDefault(str => str.RecordPropertyName == "reader" && String.Compare(str.PossibleValue, record.Reader, StringComparison.OrdinalIgnoreCase) == 0);
				if (readerReplacement != null) record.Reader = readerReplacement.ReplaceValue;

				string authorName = CalcAuthorName(record);
				var authorReplacement = cache.FirstOrDefault(str => str.RecordPropertyName == "author" && String.Compare(str.PossibleValue, authorName, StringComparison.OrdinalIgnoreCase) == 0);
				if (authorReplacement != null) record.Author = authorReplacement.ReplaceValue;
				else record.Author = authorName;

				if (record.Author == Context.AuthorDefaultName)
				{
					string[] parts = record.Title.Split(" - ");
					if (parts.Length > 1)
					{
						var possibleAuthor = parts[0].Trim().ToLower();
						string author = null;
						if (_authors.TryGetValue(possibleAuthor, out author)) record.Author = author;
					}
				}
				else _authors.TryAdd(authorName, authorName);

				string genreName = record.Genre ?? Context.GenreDefaultName;
				var genreReplacement = cache.FirstOrDefault(str => str.RecordPropertyName == "genre" && String.Compare(str.PossibleValue, genreName, StringComparison.OrdinalIgnoreCase) == 0);
				if (genreReplacement != null) record.Genre = genreReplacement.ReplaceValue;
				else record.Genre = genreName;
			}
		}

		#endregion
	}
}